/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package socof.wget;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.hyperic.sigar.NetInterfaceStat;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;
import org.hyperic.sigar.NetFlags;
import org.hyperic.sigar.NetInterfaceConfig;

/**
 *
 * @author Carlos Pinheiro
 */
public class DownloadersBalancer extends Thread
{

    private Sigar sigar = new Sigar();
    private NetInterfaceStat ifStat;
    private String ifName;
    private long currentRxTx, lastRxTx;
    private static DownloadersBalancer dc = new DownloadersBalancer();
    private static final int WAIT_TIME = 1000;
    private static final int THRESHOLD = 10000;
    
    private static final int DOWNLOADERS_LIMIT = 50;
    private static final int MAX_BANDWIDTH_ALLOCATION = 800000;
    private ResizeableSemaphore downloadersSemaphore = new ResizeableSemaphore(0);
    private long maxBandwidthAllocation = MAX_BANDWIDTH_ALLOCATION;
    private int downloadersLimit = DOWNLOADERS_LIMIT;
    
    private boolean paused = false;

    private DownloadersBalancer()
    {
        getActiveNetworkInterface();
    }

    public static DownloadersBalancer getInstance()
    {
        return dc;
    }

    @Override
    public void run()
    {
        try
        {
            ifStat = this.sigar.getNetInterfaceStat(ifName);
            lastRxTx = ifStat.getTxBytes() + ifStat.getRxBytes();

            while (!isInterrupted() && !isPaused())
            {
                try
                {
                    //proxy = SigarProxyCache.newInstance(this.sigar);
                    ifStat = this.sigar.getNetInterfaceStat(ifName);
                    currentRxTx = ifStat.getTxBytes() + ifStat.getRxBytes();
                    long sizeDownloading = currentRxTx - lastRxTx;
                    lastRxTx = currentRxTx;
                    long maxDownloading = getMaxBandwidthAllocation();
                    if (sizeDownloading > maxDownloading)
                    {
                        decreaseDownloaders();
                    }
                    else if ((sizeDownloading + THRESHOLD) < maxDownloading)
                    {
                        increaseDownloaders();
                    }
                }
                catch (Exception ex)
                {
                }

                Thread.sleep(WAIT_TIME);
            }
        }
        catch (SigarException | InterruptedException ex)
        {
        }
    }

    private void getActiveNetworkInterface()
    {
        try
        {
            // Get default interface name
            ifStat = null;
            String[] nil = this.sigar.getNetInterfaceList();
            for (String i : nil)
            {
                NetInterfaceConfig config = this.sigar.getNetInterfaceConfig(i);
                NetInterfaceStat ns = this.sigar.getNetInterfaceStat(i);
                String sFlgs = NetFlags.getIfFlagsString(config.getFlags());
                if (ns.getTxBytes() > 0
                        && sFlgs.contains("UP") && sFlgs.contains("RUNNING") 
                        && sFlgs.contains("MULTICAST") && sFlgs.contains("BROADCAST"))
                {
                    ifName = i;
                    break;
                }
            }

            if (ifName == null)
            {
                NetInterfaceConfig config = this.sigar.getNetInterfaceConfig(null);
                ifName = config.getName();
            }
        }
        catch (SigarException ex)
        {
            Logger.getLogger(DownloadersBalancer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    

    public void setMaxExecutors(int maxDownloaders)
    {
        this.getDownloadersSemaphore().resizeIfRequired(maxDownloaders);
    }
    
    public synchronized void setDownloadersLimit (int downloadersLimit)
    {
        this.downloadersLimit = downloadersLimit;
    }
    
    /**
     * Allows user to set the max bandwidth allocation
     * @param size 
     */
    public synchronized void setMaxBandwidthAllocation (long size)
    {
        this.maxBandwidthAllocation = size;
    }
    
    public synchronized long getMaxBandwidthAllocation()
    {
        return this.maxBandwidthAllocation;
    }
    
    public void decreaseDownloaders()
    {
        if (getDownloadersSemaphore().getTotalPermits() > 1)
            getDownloadersSemaphore().removePermit();
    }
    
    public void increaseDownloaders()
    {
        if (getDownloadersSemaphore().getTotalPermits() < downloadersLimit)
            getDownloadersSemaphore().addPermit();
    }
    
    public void removeAllDownloaders()
    {
        getDownloadersSemaphore().removeAllPermits();
    }
    
    public int getNumberOfDownloaders()
    {
        return getDownloadersSemaphore().getTotalPermits();
    }
    
    public int getNumberExecuting()
    {
        return getDownloadersSemaphore().getUsedPermits();
    }

    /**
     * @return the downloadersSemaphore
     */
    public ResizeableSemaphore getDownloadersSemaphore()
    {
        return downloadersSemaphore;
    }
    
    public synchronized void setPaused(boolean paused)
    {
        this.paused = paused;
        if (!this.paused)
            this.notify();
    }
    
    private synchronized boolean isPaused ()
    {
        if (this.paused)
        {
            try
            {
                this.removeAllDownloaders();
                this.wait();
            }
            catch (InterruptedException ex)
            {
                //Logger.getLogger(DownloadersBalancer.class.getName()).log(Level.SEVERE, null, ex);
            }   
        }
        
        return this.paused;
    }
}
